package com.base.lock.redis.example;

import java.util.Arrays;
import java.util.List;
import java.util.UUID;

import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.connection.RedisStringCommands.SetOption;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.core.types.Expiration;

import lombok.extern.slf4j.Slf4j;

/**
 * AutoCloseable 1.7以后才有的.
 *  可以执行自动关闭 
 * @ClassName: RedisLock
 * @Description: 
 * @Author xuyou
 * @DateTime 2020年11月28日 下午7:21:01
 */
@Slf4j
public class RedisLock implements AutoCloseable{

	private RedisTemplate redisTemplate;
	
	private String key;
	private String value;
    //单位：秒
    private int expireTime;
	
	
	public RedisLock(RedisTemplate redisTemplate,String key, int expireTime) {
		this.key = key;
		this.value = UUID.randomUUID().toString();
		this.expireTime = expireTime;
		this.redisTemplate = redisTemplate;
	}

	/**
	 *   获取分布式锁
	 * @Title: getLock
	 * @Description: 
	 * @Author xuyou
	 * @DateTime 2020年11月28日 下午7:25:06
	 * @return
	 */
	public boolean getLock(){
		RedisCallback<Boolean> redisCallback = connection ->{
			//设置redis的setnx操作 
			SetOption option = RedisStringCommands.SetOption.ifAbsent();
			//设置过期时间30S
			Expiration expiration = Expiration.seconds(expireTime);
			//把key通过redis的key序列化工具成字节数组
			byte[] redisKey =  redisTemplate.getKeySerializer().serialize(key);
			//把value通过redis的value序列化工具成字节数组
			byte[] redisValue = redisTemplate.getValueSerializer().serialize(value);
			//执行setnx操作 设置key并且同时设置过期时间 保证了原子性操作
			Boolean result = connection.set(redisKey, redisValue, expiration, option);
			return result;
		};
		Boolean lock = (Boolean) redisTemplate.execute(redisCallback);
		return lock;
	}

	/**
	 * 释放锁
	 * @Title: unLock
	 * @Description: 
	 * @Author xuyou
	 * @DateTime 2020年11月28日 下午7:27:32
	 * @return
	 */
	public boolean unLock() {
	 	String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";
        RedisScript<Boolean> redisScript = RedisScript.of(script,Boolean.class);
        List<String> keys = Arrays.asList(key);
        Boolean result = (Boolean)redisTemplate.execute(redisScript, keys, value);
        log.info("释放锁状态:{}",result);
        return result;
	}
	
	
	@Override
	public void close() throws Exception {
		unLock();
	}

}
